import json
import asyncio
from io import BytesIO
from pathlib import Path
from typing import List, Any, Dict

import httpx
from PIL import Image
from nonebot import logger

from .config import genshin_resource_config

Image.MAX_IMAGE_PIXELS = 2800000000
DATA_PATH = Path(genshin_resource_config.genshin_resource_data_path)
ICON_SIZE = (
    int(124 * genshin_resource_config.icon_size), int(134 * genshin_resource_config.icon_size)
)
ICON_OFFSET_X, ICON_OFFSET_Y = (
    int(-62 * genshin_resource_config.icon_size), int(-110 * genshin_resource_config.icon_size)
)
TRANSPORT_SIZE = (
    int(100 * genshin_resource_config.transport_size), int(100 * genshin_resource_config.transport_size)
)
TRANSPORT_OFFSET_X, TRANSPORT_OFFSET_Y = (
    int(-50 * genshin_resource_config.transport_size), int(-50 * genshin_resource_config.transport_size)
)


async def download_url(url: str) -> httpx.Response:
    async with httpx.AsyncClient() as client:
        for i in range(3):
            try:
                response = await client.get(url, timeout=20)
                response.raise_for_status()
                return response
            except Exception as e:
                logger.warning(f"获取出错{url},第{i}次重试: {e}")
                await asyncio.sleep(3)
    raise Exception(f"{url} 获取出错！")


async def paste_resource(raw_map: Image, data: Any, resource_id_list: List[int]):
    resource_image_dict: Dict[int, Image] = {
        resource_id: Image.open(DATA_PATH / "icon" / f"{resource_id}.png").resize(ICON_SIZE)
        for resource_id in resource_id_list
    }
    x_origin, y_origin = data["origin"]
    x_start, y_start = x_size, y_size = data["total_size"]
    x_end = y_end = 0
    total = 0
    for resource_point in data["point_list"]:
        if resource_point["label_id"] in resource_id_list:
            icon = resource_image_dict[resource_point["label_id"]]
            x_pos = resource_point["x_pos"] + x_origin + ICON_OFFSET_X
            y_pos = resource_point["y_pos"] + y_origin + ICON_OFFSET_Y
            raw_map.paste(icon, (int(x_pos), int(y_pos)), icon)
            total += 1

            x_start = min(x_pos, x_start)
            y_start = min(y_pos, y_start)
            x_end = max(x_pos, x_end)
            y_end = max(y_pos, y_end)
    if not total:
        raise Exception("资源数目为0")

    x_start = max(0, x_start - 200)
    y_start = max(0, y_start - 150)
    x_end = min(x_size, x_end + 200)
    y_end = min(y_size, y_end + 250)

    transport_id_list = [int(transport.name.split(".")[0]) for transport in (DATA_PATH / "transport").iterdir()]
    transport_image_dict = {
        transport_id: Image.open(DATA_PATH / "transport" / f"{transport_id}.png").resize(TRANSPORT_SIZE)
        for transport_id in transport_id_list
    }
    for resource_point in data["point_list"]:
        if resource_point["label_id"] in transport_id_list:
            icon = transport_image_dict[resource_point["label_id"]]
            x_pos = resource_point["x_pos"] + x_origin + TRANSPORT_OFFSET_X
            y_pos = resource_point["y_pos"] + y_origin + TRANSPORT_OFFSET_Y
            if x_start < x_pos < x_end and y_start < y_pos < y_end:
                raw_map.paste(icon, (int(x_pos), int(y_pos)), icon)

    return raw_map.crop((x_start, y_start, x_end, y_end)), f"在{data['name']}中找到了{total}个资源"


async def draw(map_id: str, resource_list: List[str]):
    data = (await download_url(genshin_resource_config.point_list_url % map_id)).json()["data"]
    info = (await download_url(genshin_resource_config.map_info_url % map_id)).json()["data"]["info"]
    data.update({
        **json.loads(info["detail"]),
        "name": info["name"]
    })

    resource_id_list = list(
        map(
            lambda x: x["id"],
            filter(lambda x: x["name"] in resource_list, data["label_list"])
        )
    )
    if not resource_id_list:
        raise Exception("资源数目为0")

    raw_map = Image.open(DATA_PATH / "map" / f"{map_id}.png")
    raw_map, msg = await paste_resource(raw_map, data, resource_id_list)
    bio = BytesIO()
    raw_map.save(bio, format='JPEG')
    return bio.getvalue(), msg
